package com.kk.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kk.common.PageResult;
import com.kk.common.ResultCode;
import com.kk.constrants.RedisConstants;
import com.kk.covert.UserCovert;
import com.kk.enums.StatusEnum;
import com.kk.exceptions.BusinessException;
import com.kk.mapper.SysUserMapper;
import com.kk.pojo.entity.SysRole;
import com.kk.pojo.entity.SysUser;
import com.kk.pojo.entity.SysUserRole;
import com.kk.pojo.request.LoginRequest;
import com.kk.pojo.request.UserPageRequest;
import com.kk.pojo.request.UserRegisterRequest;
import com.kk.pojo.vo.LoginUserVO;
import com.kk.pojo.vo.MenuTreeVO;
import com.kk.pojo.vo.UserListVO;
import com.kk.pojo.vo.UserPermissionVO;
import com.kk.service.*;
import com.kk.utils.JwtUtil;
import com.kk.utils.RedisUtils;
import com.kk.utils.VirtualThreadUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.math.BigInteger;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author kl
 * @version 1.0
 * @description: TODO
 * @date 2025/10/13 下午5:37
 */

@RequiredArgsConstructor
@Slf4j
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {
    private final RedisUtils redisUtils;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysUserRoleService sysUserRoleService;
    @Autowired
    private SysMenuService sysMenuService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    private final SysLoginLogService loginLogService;





    @Override
    public LoginUserVO loginService(LoginRequest request) {
        // 再次判断一遍，参数不能为空
        if (!StringUtils.hasText(request.getUsername()) || !StringUtils.hasText(request.getPassword())) {
            // 抛出异常
            log.warn("请求参数为空，登陆失败");
            throw new BusinessException(ResultCode.FAILED.getCode(), "参数错误");

        }
        // 拿到用户名和密码，根据用户名查找
        SysUser user = getUserByUsername(request.getUsername());
        // 判断查找出来是否为空以及状态是否为可用
        if (ObjectUtils.isEmpty(user)) {
            // 抛出异常，用户不存在
            log.warn("登录用户不存在:{}",request.getUsername());
            throw new BusinessException("用户不存在");
        }
        System.out.println(passwordEncoder.encode("admin123"));
        // 如果不为空，那么判断密码是否正确
        if (!passwordEncoder.matches(request.getPassword(), user.getPassword())) {
            log.warn("登录失败：密码错误, username={}", request.getUsername());
            throw new BusinessException("用户名或密码错误");
            // 如果不相等，那么就抛出异常，密码错误
        }
        // 如果相等那么就是登陆成功，会做下面几件事情
        //    1. 生成token,存放用户名，用户id等信息
        HashMap<String, Object> map = new HashMap<>();
        map.put("id", user.getId());
        map.put("username", user.getUsername());
        String token = JwtUtil.create(user.getUsername(), map);
        //    2. 封装结果返回
        LoginUserVO loginUserVO = UserCovert.UserToVO(user);
        loginUserVO.setToken(token);

        //    3. 查询其权限以及目录保存在redis
        // 根据用户id查询其权限
//        List<SysUserRole> sysUserRoles = sysUserRoleService.selectRoleByUserId(user.getId());

        // 传入ids
        loadAndCacheUserPermissionsAsync(user.getId());
        loginLogService.recordLoginSuccess(user.getId(),user.getUsername(), "登录成功");
        // 返回VO
        return loginUserVO;
    }

    private void loadAndCacheUserPermissionsAsync(BigInteger userId) {
        VirtualThreadUtil.runAsync(() -> {
            try {
                log.info("开始异步加载用户权限，userId={}", userId);

                // ✅ 并发执行三个查询（每个都是优化后的连表查询）
                CompletableFuture<List<MenuTreeVO>> menuFuture =
                        VirtualThreadUtil.supplyAsync(() -> sysMenuService.getMenuTreeByUserId(userId));

                CompletableFuture<Set<String>> permissionFuture =
                        VirtualThreadUtil.supplyAsync(() -> sysMenuService.getPermissionsByUserId(userId));

                CompletableFuture<Set<String>> rolesFuture =
                        VirtualThreadUtil.supplyAsync(() -> sysRoleService.getUserRoleCodes(userId));

                CompletableFuture.allOf(menuFuture, permissionFuture, rolesFuture).join();

                UserPermissionVO permissionVO = new UserPermissionVO();
                permissionVO.setMenus(menuFuture.get());
                permissionVO.setPermissions(permissionFuture.get());
                permissionVO.setRoles(rolesFuture.get());

                String redisKey = RedisConstants.USER_PREFIX + userId;
                redisUtils.set(redisKey, permissionVO, RedisConstants.CACHE_EXPIRE_TIME, TimeUnit.MINUTES);

                log.info("✅ 缓存成功: userId={}, menus={}, permissions={}, roles={}",
                        userId, permissionVO.getMenus().size(),
                        permissionVO.getPermissions().size(),
                        permissionVO.getRoles().size());

            } catch (Exception e) {
                log.error("❌ 异步加载用户权限失败，userId={}", userId, e);
            }
        });
    }


    @Override
    // 根据用户名查找用户
    public SysUser getUserByUsername(String username) {
        LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SysUser::getUsername, username);
        return this.baseMapper.selectOne(queryWrapper);
    }

    /**
     * 注册方法
     * @return 是否成功
     */
    @Override
    @Transactional
    public Boolean registerService(UserRegisterRequest request) {
        // 因为controller已经判断过了，这里就不判断了
        // 先看用户名，手机号，邮箱是否存在
        checkUserExits(request.getUsername(),request.getEmail(),request.getPhone());
        // 加密密码
        String newPassword = passwordEncoder.encode(request.getPassword());
        // 构建存储的对象
        SysUser user = UserCovert.convertRegisterToUser(request);
        user.setPassword(newPassword);
        user.setStatus(StatusEnum.ENABLED.getValue());
        // 保存到数据库
        boolean save = this.save(user);
        if (!save){
            throw new BusinessException("注册失败，请稍后重试");
        }
        // 分配角色
        sysRoleService.assignRoleToUser(user.getId());
        return true;
    }


    private void checkUserExits(String username,String email,String phone) {
        // 检查用户名（必填）
        if (this.exists(new LambdaQueryWrapper<SysUser>()
                .eq(SysUser::getUsername, username))) {
            throw new BusinessException("用户名已存在");
        }

        // 检查邮箱
        if (StringUtils.hasText(email) &&
                this.exists(new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getEmail, email))) {
            throw new BusinessException("邮箱已被注册");
        }

        // 检查手机号
        if (StringUtils.hasText(phone) &&
                this.exists(new LambdaQueryWrapper<SysUser>()
                        .eq(SysUser::getPhone, phone))) {
            throw new BusinessException("手机号已被注册");
        }

    }

    /**
     * 获取用户角色编码
     */
    private Set<String> getUserRoleCodes(BigInteger userId) {
        List<SysUserRole> userRoles = sysUserRoleService.selectRoleByUserId(userId);
        if (userRoles == null || userRoles.isEmpty()) {
            return Collections.emptySet();
        }

        List<BigInteger> roleIds = userRoles.stream()
                .map(SysUserRole::getRoleId)
                .collect(Collectors.toList());

        List<SysRole> roles = sysRoleService.findRoleByUserName(roleIds);
        return roles.stream()
                .map(SysRole::getRoleCode) // 假设SysRole有roleCode字段
                .collect(Collectors.toSet());
    }

    /**
     * 用户登出 - 清除缓存
     */
    public void logout(BigInteger userId) {
        // 清除所有相关缓存
//        redisUtils.delete(RedisConstants.LOGIN_USER_KEY + userId);
//        redisUtils.delete(RedisConstants.USER_PERMISSION_KEY + userId);
//        redisUtils.delete(RedisConstants.USER_MENU_TREE_KEY + userId);

        log.info("用户登出成功，缓存已清除: userId={}", userId);
    }



    @Override
    public PageResult<UserListVO> getUserPage(UserPageRequest request) {
        LambdaQueryWrapper<SysUser> wrapper = Wrappers.<SysUser>lambdaQuery()
                .like(StringUtils.hasText(request.getUsername()), SysUser::getUsername, request.getUsername())
                .like(StringUtils.hasText(request.getNickname()), SysUser::getNickname, request.getNickname())
                .eq(StringUtils.hasText(request.getPhone()), SysUser::getPhone, request.getPhone())
                .eq(StringUtils.hasText(request.getEmail()), SysUser::getEmail, request.getEmail())
                .eq(request.getStatus() != null, SysUser::getStatus, request.getStatus())
                .ge(request.getCreateTimeStart() != null, SysUser::getCreateTime, request.getCreateTimeStart())
                .le(request.getCreateTimeEnd() != null, SysUser::getCreateTime, request.getCreateTimeEnd())
                .orderByDesc(SysUser::getCreateTime); // 简化：固定排序

        // 执行分页查询
        // 分页查询
        Page<SysUser> page = this.page(new Page<>(request.getPageNum(), request.getPageSize()), wrapper);

        return PageResult.of(page, user -> {
            UserListVO vo = new UserListVO();
            BeanUtils.copyProperties(user, vo);
            return vo;
        });
    }



}
